#pragma once
#include "includeCL.h"

#include <boost/random.hpp>
#include <boost/shared_ptr.hpp>
#include <time.h>


namespace Algorithm
{

static const cl_float MAX_DATA_POINC_VECTOR_ELEMENT=1000.0f;
static const cl_float DATA_POINC_VECTOR_ELEMENT_EPSILON=0.1f;

template<unsigned int OBSERVATION_VECTOR_SIZE, unsigned int NUMBER_OF_OBSERVATIONS, unsigned int NUMBER_OF_CENTROIDS >
class AlgoritmhInput
{
public:

	template <class DATA_TYPE,unsigned int ARRAY_SIZE1>
	class WrappedArray1
	{
	public:
		typedef WrappedArray1<DATA_TYPE, ARRAY_SIZE1> TMyType;

		DATA_TYPE data[ARRAY_SIZE1];

		bool operator==(const TMyType& _another)
		{
			for(int i=0;i<ARRAY_SIZE1;i++)
			{
				if(!observationVectorElementsEqual(this->data[i], _another.data[i]))return false;
			}
			return true;
		}

		void initialize(const DATA_TYPE& _initializer)
		{
			for(int i=0;i<ARRAY_SIZE1;i++)
			{
				this->data[i]=((DATA_TYPE)_initializer);
			}
		}
	};

	template <class DATA_TYPE,unsigned int ARRAY_SIZE1 ,unsigned int ARRAY_SIZE2>
	class WrappedArray2
	{
	public:
		typedef WrappedArray2<DATA_TYPE, ARRAY_SIZE1, ARRAY_SIZE2> TMyType;
		typedef WrappedArray2<DATA_TYPE, ARRAY_SIZE2, ARRAY_SIZE1> TTransposed;
		typedef boost::shared_ptr<TTransposed> TTransposedSharedPtr;

		DATA_TYPE data[ARRAY_SIZE1][ARRAY_SIZE2];

		bool operator==(const TMyType& _another)
		{
			for(int i=0;i<ARRAY_SIZE1*ARRAY_SIZE2;i++)
			{
				if(!observationVectorElementsEqual(((DATA_TYPE*)this->data)[i], ((DATA_TYPE*)_another.data)[i]))return false;
			}
			return true;
		}

		void initialize(const DATA_TYPE& _initializer)
		{
			for(int i=0;i<ARRAY_SIZE1*ARRAY_SIZE2;i++)
			{
				((DATA_TYPE*)this->data)[i]=((DATA_TYPE)_initializer);
			}
		}

		TTransposedSharedPtr calculateTransposed()
		{
			TTransposedSharedPtr returned(new TTransposed());
			for(int i=0;i<ARRAY_SIZE1;i++)
				for(int j=0;j<ARRAY_SIZE2;j++)
				{
					(returned->data[j][i])=data[i][j];
				}

			return returned;
		}

	};

	typedef WrappedArray2<cl_float, NUMBER_OF_OBSERVATIONS, OBSERVATION_VECTOR_SIZE> TObservations;
	typedef WrappedArray2<cl_float, NUMBER_OF_CENTROIDS, OBSERVATION_VECTOR_SIZE> TCentroids;

	static bool observationVectorElementsEqual(const cl_float& _x1, const cl_float& _x2)
	{
		return (_x1-_x2<=DATA_POINC_VECTOR_ELEMENT_EPSILON)
			&&(_x2-_x1<=DATA_POINC_VECTOR_ELEMENT_EPSILON);
	}

	static bool observationVectorElementsEqual(const cl_uint& _x1, const cl_uint& _x2)
	{
		return _x1==_x2;
	}

	static void generateAlgorithmInputRandom(TObservations& _observations,TCentroids& _centroids)
	{
		boost::mt19937 private_generator;   
		private_generator.seed((unsigned int)time (NULL));
		boost::uniform_real<cl_float> distribution(0.0f,MAX_DATA_POINC_VECTOR_ELEMENT) ; 
		boost::variate_generator<boost::mt19937&, boost::uniform_real<cl_float> >
				generator(private_generator, distribution);            
		
		for(int i=0;i<NUMBER_OF_OBSERVATIONS*OBSERVATION_VECTOR_SIZE;i++)
		{
			((cl_float*)_observations.data)[i]=generator();
		}

		for(int i=0;i<NUMBER_OF_CENTROIDS*OBSERVATION_VECTOR_SIZE;i++)
		{
			((cl_float*)_centroids.data)[i]=generator();
		}

	}
};

}//namespace Algorithm
